Skip to content

fix(native): thread config.db.busyTimeoutMs into NativeDatabase open factories#2021

Open
carlos-alm wants to merge 1 commit into
fix/issue-1881-loadconfig-resolves-from-process-cwd-insteadfrom
fix/issue-1882-thread-config-db-busytimeoutms-into-the-rust
Open

fix(native): thread config.db.busyTimeoutMs into NativeDatabase open factories#2021
carlos-alm wants to merge 1 commit into
fix/issue-1881-loadconfig-resolves-from-process-cwd-insteadfrom
fix/issue-1882-thread-config-db-busytimeoutms-into-the-rust

Conversation

@carlos-alm

Copy link
Copy Markdown
Contributor

Summary

NativeDatabase::open_readonly() / open_read_write() in crates/codegraph-core/src/db/connection.rs hardcoded PRAGMA busy_timeout = 5000, ignoring config.db.busyTimeoutMs entirely — the Rust-side gap deferred from #1763 (which wired every TS-side openReadonlyOrFail()/better-sqlite3 call site but explicitly left this to a follow-up, since it requires a native rebuild).

What changed

  1. Rust: open_readonly() / open_read_write() now take an optional busy_timeout_ms: Option<u32> parameter, applied via the busy_timeout pragma; falls back to a new DEFAULT_BUSY_TIMEOUT_MS = 5000 constant when omitted (mirrors DEFAULTS.db.busyTimeoutMs in src/infrastructure/config.ts).
  2. napi codegen: rebuilt the native addon (napi build --platform --release) and updated src/types.ts's NativeAddon.NativeDatabase interface to match the new signature (crates/codegraph-core/index.d.ts itself is gitignored/generated).
  3. TS call sites (all 6 identified in Thread config.db.busyTimeoutMs into the Rust native DB layer (NativeDatabase::open_readonly/open_read_write) #1882) now pass the already-resolved value through:
    • src/db/connection.ts: openRepoNative/tryOpenRepoNative (→ openRepo()) and openReadonlyWithNative() — both already had busyTimeoutMs in scope via resolveDbSettings().
    • src/domain/graph/builder/stages/native-db-lifecycle.ts: reopenNativeDb() — build-path call site, uses ctx.config.db.busyTimeoutMs.
    • src/domain/graph/builder/stages/native-orchestrator.ts: both openReadWrite() call sites — same ctx.config.db.busyTimeoutMs pattern.
    • src/features/branch-compare.ts: openNativeDbForFanMetrics() previously did no config resolution at all — added a resolveBusyTimeoutMs(dbPath) call, following the pattern db.busyTimeoutMs: extend config-driven wiring to read-only query paths and Rust connection.rs #1763 established for ad-hoc call sites.

Out of scope (filed separately)

While verifying the fix I found two pre-existing, unrelated bugs and filed them rather than fixing them here:

Test plan

  • New tests/unit/native-db-busy-timeout.test.ts — exercises the real compiled native addon: openReadWrite/openReadonly apply a passed busyTimeoutMs, and default to DEFAULTS.db.busyTimeoutMs (5000) when omitted. Verified via queryGet('PRAGMA busy_timeout', []) (not pragma(), see NativeDatabase.pragma() crashes on any PRAGMA that returns a non-TEXT value #2019).
  • New tests/unit/native-db-busy-timeout-threading.test.ts — mocks infrastructure/native.js (same pattern as tests/unit/openRepo-busy.test.ts) and asserts openRepo(), openReadonlyWithNative(), and reopenNativeDb() each pass the project-configured busyTimeoutMs through to the native factory call.
  • npm test — full suite green (243 test files, 3899 passed, 0 failed, 30 skipped/2 todo pre-existing).
  • npm run lint — clean (only a pre-existing unrelated warning in a fixture file I didn't touch).
  • npx tsc --noEmit and npm run build — clean.
  • cargo check --release and cargo clippy --release on the crate — no new warnings on the changed lines; cargo fmt --check shows no diff in the functions I touched.
  • Manual end-to-end: opened a temp DB with a custom busyTimeoutMs via the rebuilt native addon directly and confirmed the pragma value round-trips correctly for both openReadWrite and openReadonly, with and without the parameter.
  • codegraph diff-impact --staged -T confirms the blast radius matches exactly the 6 call sites plus their transitive callers — no unexpected symbols touched.

Note: the platform-specific prebuilt binaries (@optave/codegraph-{platform}-{arch}) still need to be rebuilt and republished for this fix to reach installs that use the npm-published native addon rather than a locally compiled one — that rides the normal release cadence per #1882's own scope note, not part of this PR.

Fixes #1882

…factories

NativeDatabase::open_readonly/open_read_write hardcoded PRAGMA
busy_timeout = 5000 in the Rust native DB layer, ignoring
config.db.busyTimeoutMs entirely (#1763 only wired the TS-side
better-sqlite3 pragma). Add an optional busy_timeout_ms param to both
factories, defaulting to 5000 when omitted, and thread the
already-resolved value through all six JS call sites: openRepo (via
openRepoNative/tryOpenRepoNative) and openReadonlyWithNative in
db/connection.ts, reopenNativeDb in native-db-lifecycle.ts, the two
NativeDatabase.openReadWrite call sites in native-orchestrator.ts, and
branch-compare.ts's fan-metrics helper (which previously did no config
resolution at all).

Impact: 9 functions changed, 47 affected
@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR closes the Rust-side gap deferred from #1763 by threading config.db.busyTimeoutMs through the two NativeDatabase factory functions (open_read_write / open_readonly) in the Rust crate and updating all 6 TypeScript call sites that open a native DB handle.

  • Rust: open_read_write and open_readonly now accept busy_timeout_ms: Option<u32>, falling back to a new DEFAULT_BUSY_TIMEOUT_MS = 5000 constant; the value is interpolated into the PRAGMA string via format!() — safe because the type is u32 (digits-only).
  • TypeScript call sites: All 6 sites pass the already-resolved busyTimeoutMs from resolveDbSettings() / ctx.config.db.busyTimeoutMs; branch-compare.ts adds a resolveBusyTimeoutMs(dbPath) call following the pattern established in db.busyTimeoutMs: extend config-driven wiring to read-only query paths and Rust connection.rs #1763.
  • Tests: A real-addon integration test and a mock-based threading test cover the default/custom timeout round-trip and argument forwarding for 3 of the 6 call sites.

Confidence Score: 4/5

Safe to merge; all 6 changed call sites correctly forward the resolved timeout and the fallback constant mirrors the TypeScript default.

The implementation is mechanically straightforward and consistent across all changed files. The threading test covers openRepo, openReadonlyWithNative, and reopenNativeDb, but leaves the two native-orchestrator.ts call sites and openNativeDbForFanMetrics in branch-compare.ts without dedicated forwarding assertions — a regression there would be invisible to the new test suite.

tests/unit/native-db-busy-timeout-threading.test.ts — only 3 of the 6 changed call sites have threading assertions; src/domain/graph/builder/stages/native-orchestrator.ts and src/features/branch-compare.ts are the uncovered paths.

Important Files Changed

Filename Overview
crates/codegraph-core/src/db/connection.rs Adds busy_timeout_ms: Option<u32> to both open_read_write and open_readonly, falling back to DEFAULT_BUSY_TIMEOUT_MS = 5000. Uses format!() to interpolate the u32 into the PRAGMA string — safe since u32 is digits-only.
src/db/connection.ts Threads busyTimeoutMs (already resolved via resolveDbSettings()) into openRepoNative, tryOpenRepoNative, and the openReadonlyWithNative native path. Clean, correct propagation with no behavioural changes.
src/domain/graph/builder/stages/native-db-lifecycle.ts Single-line change: forwards ctx.config.db.busyTimeoutMs to openReadWrite. Correct; ctx.config is always resolved before this stage executes.
src/domain/graph/builder/stages/native-orchestrator.ts Both openNativeDatabase and runPostNativeAnalysis now pass ctx.config.db.busyTimeoutMs to openReadWrite. Changes are mechanically consistent with the other call sites.
src/features/branch-compare.ts Adds resolveBusyTimeoutMs(dbPath) call to openNativeDbForFanMetrics, following the pattern established by #1763 for ad-hoc call sites that don't have a pre-resolved config in scope.
src/types.ts TypeScript interface for NativeAddon.NativeDatabase updated to declare busyTimeoutMs?: number as an optional second argument on both factory methods, matching the new napi-generated signature.
tests/unit/native-db-busy-timeout-threading.test.ts Mocks the native addon and asserts that openRepo, openReadonlyWithNative, and reopenNativeDb forward the configured busyTimeoutMs. The two native-orchestrator.ts call sites and branch-compare.ts are not covered, leaving 3 of 6 changed call sites without a threading assertion.
tests/unit/native-db-busy-timeout.test.ts Integration tests against the real compiled native addon; skipped when native is unavailable. Verifies both openReadWrite and openReadonly apply a custom timeout and fall back to DEFAULTS.db.busyTimeoutMs when omitted, using queryGet to avoid the pragma() TEXT-only limitation (#2019).

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant TS as TypeScript call site
    participant Cfg as resolveDbSettings / resolveBusyTimeoutMs
    participant NAddon as NativeAddon (JS bridge)
    participant Rust as NativeDatabase (Rust/napi)
    participant SQLite as SQLite

    TS->>Cfg: resolve busyTimeoutMs from config
    Cfg-->>TS: busyTimeoutMs (e.g. 424242)
    TS->>NAddon: NativeDatabase.openReadonly(dbPath, busyTimeoutMs)
    NAddon->>Rust: open_readonly(db_path, Some(busy_timeout_ms))
    Rust->>SQLite: "PRAGMA busy_timeout = 424242"
    SQLite-->>Rust: ok
    Rust-->>NAddon: NativeDatabase instance
    NAddon-->>TS: nativeDb handle
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant TS as TypeScript call site
    participant Cfg as resolveDbSettings / resolveBusyTimeoutMs
    participant NAddon as NativeAddon (JS bridge)
    participant Rust as NativeDatabase (Rust/napi)
    participant SQLite as SQLite

    TS->>Cfg: resolve busyTimeoutMs from config
    Cfg-->>TS: busyTimeoutMs (e.g. 424242)
    TS->>NAddon: NativeDatabase.openReadonly(dbPath, busyTimeoutMs)
    NAddon->>Rust: open_readonly(db_path, Some(busy_timeout_ms))
    Rust->>SQLite: "PRAGMA busy_timeout = 424242"
    SQLite-->>Rust: ok
    Rust-->>NAddon: NativeDatabase instance
    NAddon-->>TS: nativeDb handle
Loading

Fix All in Claude Code

Reviews (1): Last reviewed commit: "fix(native): thread config.db.busyTimeou..." | Re-trigger Greptile

Comment on lines +109 to +121
it('passes ctx.config.db.busyTimeoutMs to the native factory', () => {
openReadWriteCalls.length = 0;
const ctx = new PipelineContext();
ctx.dbPath = dbPath;
ctx.opts = { engine: 'native' };
ctx.config = { db: { busyTimeoutMs: CUSTOM_BUSY_TIMEOUT_MS } } as PipelineContext['config'];

reopenNativeDb(ctx, 'test');

expect(openReadWriteCalls).toHaveLength(1);
expect(openReadWriteCalls[0]?.[1]).toBe(CUSTOM_BUSY_TIMEOUT_MS);
});
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Threading test covers 3 of 6 call sites

The threading test asserts that openRepo, openReadonlyWithNative, and reopenNativeDb all forward busyTimeoutMs. However, three changed call sites are not exercised here: openNativeDatabase (~line 1955 in native-orchestrator.ts), runPostNativeAnalysis (~line 579 in native-orchestrator.ts), and openNativeDbForFanMetrics in branch-compare.ts. A future regression in those paths would go unnoticed by this test suite.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Codegraph Impact Analysis

9 functions changed47 callers affected across 32 files

  • openRepoNative in src/db/connection.ts:477 (6 transitive callers)
  • tryOpenRepoNative in src/db/connection.ts:532 (17 transitive callers)
  • openRepo in src/db/connection.ts:577 (28 transitive callers)
  • openReadonlyWithNative in src/db/connection.ts:606 (8 transitive callers)
  • reopenNativeDb in src/domain/graph/builder/stages/native-db-lifecycle.ts:42 (4 transitive callers)
  • runPostNativeAnalysis in src/domain/graph/builder/stages/native-orchestrator.ts:559 (4 transitive callers)
  • openNativeDatabase in src/domain/graph/builder/stages/native-orchestrator.ts:1948 (3 transitive callers)
  • openNativeDbForFanMetrics in src/features/branch-compare.ts:120 (3 transitive callers)
  • NativeAddon.NativeDatabase in src/types.ts:2392 (0 transitive callers)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant